import {
    OrbitControls
} from 'three/examples/jsm/controls/OrbitControls';
import {
    Scene,
    PerspectiveCamera,
    WebGLRenderer,
    AxesHelper,
    Mesh,
    TextureLoader,
    HemisphereLight,
    BufferGeometry,
    BufferAttribute,
    MeshBasicMaterial,
    DoubleSide,
    PlaneGeometry,
    Vector3,
    Group,
    Euler,
    MathUtils,
    Color,
    ShapeGeometry,
    MeshLambertMaterial,
    RepeatWrapping,
    MeshPhongMaterial
} from 'three';
import {
    CSS3DRenderer,
    CSS3DObject
} from 'three/examples/jsm/renderers/CSS3DRenderer.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';

import dataLists from './data.js'
export default class Crossroads {
    constructor(dom) {
        this.H = 6000; // 分支的长度
        this.laneWidth = 26; // 每条车道的宽度
        this.rxdWidth = 10 // 行人道长度
        this.rxdHeight = 1 // 行人道宽度
        this.shxWidth = 1 // 双黄线宽度
        this.dataLists = dataLists // 渠化数据

        this.scene = new Scene();
        this.scene2 = new Scene();
        this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
        this.renderer = new WebGLRenderer({
            antialias: true
        });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setPixelRatio(window.devicePixelRatio);
        dom.appendChild(this.renderer.domElement);
        this.camera.position.set(0, 100, -100);

        this.renderer2 = new CSS3DRenderer();
        this.renderer2.setSize(window.innerWidth, window.innerHeight);
        this.renderer2.domElement.style.position = 'absolute';
        this.renderer2.domElement.style.top = 0;
        this.renderer2.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(this.renderer2.domElement);

        const controls = new OrbitControls(this.camera, this.renderer2.domElement); // 控制器
        controls.maxPolarAngle = Math.PI * 0.5;
        controls.minDistance = 10;
        controls.maxDistance = 500;
        const axesHelper = new AxesHelper(5000); // 辅助线
        // this.scene.add(axesHelper);

        // 光源
        // const light = new HemisphereLight(0xffffff, 0x444444);
        // this.scene.add(light);

        // 添加环境
        this.textLoader = new TextureLoader();
        const textcube = this.textLoader.load('/static/sky1.jpg');
        this.scene.background = textcube;

        // 添加草坪
        this.createCp()
        // 创建十字路口
        this.createCross()
        this.render();
    }

    createCp() {
        let textureTest = this.textLoader.load('/static/grasslight-big.jpg');
        textureTest.repeat.set(25, 25);
        textureTest.rotation = Math.PI;
        textureTest.wrapS = RepeatWrapping; // ClampToEdgeWrapping; //RepeatWrapping; //MirroredRepeatWrapping
        textureTest.wrapT = RepeatWrapping; // ClampToEdgeWrapping; //RepeatWrapping; //MirroredRepeatWrapping
        const mesh = new Mesh(new PlaneGeometry(20000, 20000), new MeshBasicMaterial({ map: textureTest, side: DoubleSide }));
        mesh.position.y = 0;
        mesh.rotation.x = - Math.PI / 2;
        this.scene.add(mesh);
    }

    // 创建十字路口
    createCross() {
        // 创建路口先创建一个十字路口
        this.dataLists.forEach(item => {
            // 旋转是逆时针旋转的
            const branch = this.setCross(item) // 北
            branch.rotation.y = item.angle * MathUtils.DEG2RAD
            // 获取旋转后四个点的坐标
            item.branchCoord = this.getRotateDeg(item)
            this.scene.add(branch)
        })
        // 获取分支交点坐标然后画线
        this.getAllBranchDot()
    }

    // 获取分支交点坐标然后画线
    getAllBranchDot() {
        let lists = this.dataLists
        for (let i = 0; i < lists.length; i++) {
            let branchData = [] // 分支数据
            for (let k = 0; k < lists.length; k++) {
                if (k == i) continue; // 如果是本分支则不参与计算
                let angle = Math.abs(this.getNearBranchByLeft(lists[i].branchCoord.lt, lists[k].branchCoord.rt))
                branchData.push({
                    ...lists[k],
                    angleBranch: angle
                })
            }
            const minAngle = Math.min(...branchData.map(item => item.angleBranch))
            const maxAngle = Math.max(...branchData.map(item => item.angleBranch))
            const nearBranchDataLeft = branchData.find(item => item.angleBranch == maxAngle)
            const nearBranchDataRight = branchData.find(item => item.angleBranch == minAngle)

            // 获取分支交点
            lists[i].leftJd = lists[i].leftJd || this.segmentsIntr(lists[i].branchCoord.lt, lists[i].branchCoord.lb, nearBranchDataLeft.branchCoord.rt, nearBranchDataLeft.branchCoord.rb)
            lists[i].rightJd = lists[i].rightJd || this.segmentsIntr(lists[i].branchCoord.rt, lists[i].branchCoord.rb, nearBranchDataRight.branchCoord.lt, nearBranchDataRight.branchCoord.lb)
            // 画人行道和双黄线
            this.createPavement(lists[i])
        }

    }

    // 人行道
    createPavement(item) {
        // 因为需要创建物体坐标系所以点位的经纬度会产生变化以v1为坐标原点,所以坐标
        let vector = new Vector3(item.leftJd.x, 0, item.leftJd.y)
        const vector1 = vector.clone().sub(vector) // 左交点相对位置
        const vector2 = new Vector3(item.rightJd.x, 0, item.rightJd.y).sub(vector)
        const vector3 = new Vector3(item.branchCoord.lb.x, 0, item.branchCoord.lb.y).sub(vector)
        const vector4 = new Vector3(item.branchCoord.lt.x, 0, item.branchCoord.lt.y).sub(vector)
        const angle1 = this.getVector(vector1, vector2) // 伸展角度
        const angle2 = this.getVector(vector3, vector4) // 分支伸展角度
        const _this = this


        // 创建物体坐标系
        const axesHelper1 = new AxesHelper(200); // 辅助线
        axesHelper1.position.copy(vector)
        // this.scene.add(axesHelper1);

        const allLength = item.lanes.length * this.laneWidth // 总长度
        let num = Math.floor(allLength / (this.rxdHeight + 1))
        let angle3 = 360 - item.angle // 将单个人行道平面中心点平移到右上角

        // 旋转人行道中右上角坐标
        let cood = { x: (- this.rxdHeight / 2), y: (this.rxdWidth / 2) }
        let dCo = this.rotateCoods(cood, angle3) // 旋转后的坐标
        let rtCood = new Vector3(dCo.x1, 0, dCo.y1) // 右上角坐标

        const inLanes = item.lanes.filter(item => !item.isOut) // 进车道
        let xPaceLine = (vector2.x - vector1.x) / item.lanes.length // 每条车道距离
        let yPaceLine = (vector2.z - vector1.z) / item.lanes.length // 每条车道距离

        let coodA = { x: (this.rxdHeight / 2), y: (this.rxdWidth / 2) } // 斑马线左上角坐标
        let dCoG = this.rotateCoods(coodA, angle3) // 旋转后的坐标
        let lfCoodT = new Vector3((dCoG.x1 + dCo.x1), 0, (dCoG.y1 + dCo.y1)) // 直线左边坐标

        createBmx()  // 创建斑马线
        createBmxFx() // 创建出车道斑马线前的直线
        createCarRoad() // 创建车道边线
        createShx()  // 创建双黄线
        turnSvg() // 转向图标

        function createBmx() {
            // 斑马线
            for (let i = 0; i < num; i++) {
                const group = new Group();

                // 人行道
                const plane = _this.createPlane(
                    _this.rxdWidth, _this.rxdHeight,
                    '#999',
                    vector.clone(),
                    new Euler(-90 * MathUtils.DEG2RAD, 0, (180 - angle2) * MathUtils.DEG2RAD)
                )

                group.add(plane.mesh)
                group.add(plane.object)
                group.position.copy(rtCood)

                // 每个矩形按顺序递增
                let xPace = (vector2.x - vector1.x) / num
                let zPace = (vector2.z - vector1.z) / num
                group.translateX(i * xPace)
                group.translateZ(i * zPace)

                _this.scene2.add(group)
            }
        }

        function createShx() {
            // 创建双黄线
            lineBasic(_this.shxWidth, _this.H, inLanes.length, 'orange')
        }

        function createBmxFx() {
            let rCoodX = lfCoodT.x + xPaceLine * inLanes.length
            let rCoodZ = lfCoodT.z + yPaceLine * inLanes.length
            let rCoodT = new Vector3(rCoodX, 0, rCoodZ) // 直线右边坐标
            const lineT = _this.createLine(lfCoodT.clone().add(vector), rCoodT.clone().add(vector), '#999')
            _this.scene.add(lineT)
        }

        function createCarRoad() {
            item.lanes.forEach((child, index) => {
                if (index + 1 == item.lanes.length || index + 1 == inLanes.length) return  // 最后一条和中间黄线不画
                let lineT
                let sCoodX = lfCoodT.x + xPaceLine * (index + 1)
                let sCoodZ = lfCoodT.z + yPaceLine * (index + 1)
                let sCoodT = new Vector3(sCoodX, 0, sCoodZ).add(vector) // 这个作为车道起点
                const endX = 500 * Math.cos(angle2 * MathUtils.DEG2RAD) + sCoodX
                const endY = 500 * Math.sin(angle2 * MathUtils.DEG2RAD) + sCoodZ
                let eEndT = new Vector3(endX, 0, endY).add(vector) // 这个作为车道终点
                // 注意出车道为实线 进车道为虚线
                if (index + 1 < inLanes.length) {
                    // 出车道
                    lineT = _this.createLine(sCoodT, eEndT, '#999')
                } else {
                    // 进车道
                    lineT = _this.createDashedLine(sCoodT, eEndT, '#999')
                }
                _this.scene.add(lineT)
            })
        }

        // 创建线基础方法
        function lineBasic(W, H, num, bgColor) {
            const lineGroup = new Group()
            const linePlane = _this.createPlane(
                W, H,
                bgColor,
                vector.clone(),
                new Euler(-90 * MathUtils.DEG2RAD, 0, item.angle * MathUtils.DEG2RAD)
            )
            lineGroup.add(linePlane.mesh)
            lineGroup.add(linePlane.object)

            let dCoT = _this.rotateCoods({ x: (- _this.shxWidth / 2), y: (_this.H / 2) }, angle3) // 双黄线右上角旋转后的坐标
            let dCoB = _this.rotateCoods({ x: 0, y: _this.rxdWidth / 2 }, angle3) // 斑马线正上角旋转后的坐标 
            let cop = new Vector3(dCoT.x1 + 2 * dCoB.x1, 0, dCoT.y1 + 2 * dCoB.y1) // 右上角坐标
            lineGroup.position.copy(cop)

            lineGroup.translateX(xPaceLine * num)
            lineGroup.translateZ(yPaceLine * num)
            _this.scene2.add(lineGroup)
        }

        // 
        function turnSvg() {
            const loader = new SVGLoader();
            item.lanes.forEach((child, index) => {
                let url
                url = (child.isOut && `static/svg/${child.fxfx}.svg`) || `static/svg/1.svg`
                loader.load(url, function (data) {
                    const paths = data.paths;
                    const group = new Group();
                    let space = 0 // 偏移距离
                    group.scale.multiplyScalar(0.01);
                    if (child.isOut) {
                        space = 20
                        group.rotation.copy(new Euler(-90 * MathUtils.DEG2RAD, 0, (item.angle + 180) * MathUtils.DEG2RAD))
                    } else {
                        space = 10
                        group.rotation.copy(new Euler(-90 * MathUtils.DEG2RAD, 0, item.angle * MathUtils.DEG2RAD))
                    }


                    // 根据车道来定所在位置
                    let startX = lfCoodT.x + xPaceLine * index
                    let startZ = lfCoodT.z + yPaceLine * index
                    const endX = lfCoodT.x + xPaceLine * (index + 1) // 下一个车道的x起点
                    const endZ = lfCoodT.z + yPaceLine * (index + 1) // 下一个车道的z起点
                    const moX = space * Math.cos(angle2 * MathUtils.DEG2RAD) + (startX + endX) / 2 + vector.x
                    const moZ = space * Math.sin(angle2 * MathUtils.DEG2RAD) + (startZ + endZ) / 2 + vector.z

                    group.position.set(moX, 0, moZ)

                    // 图标本身有偏移
                    group.translateX(-5)
                    group.translateY(-10.5)


                    for (let i = 0; i < paths.length; i++) {
                        const path = paths[i];
                        const fillColor = path.userData.style.fill;
                        if (fillColor !== undefined && fillColor !== 'none') {
                            const material = new MeshBasicMaterial({
                                color: new Color().setStyle(fillColor),
                                opacity: path.userData.style.fillOpacity,
                                side: DoubleSide,
                                depthWrite: false,
                                wireframe: 14
                            });

                            const shapes = path.toShapes(true);

                            for (let j = 0; j < shapes.length; j++) {

                                const shape = shapes[j];

                                const geometry = new ShapeGeometry(shape);
                                const mesh = new Mesh(geometry, material);

                                mesh.renderOrder = 9
                                mesh.material.depthTest = false;

                                group.add(mesh);

                            }

                        }
                    }
                    _this.scene.add(group)
                })
            })



        }

    }

    // 旋转算法
    rotateCoods(a, angle) {
        const { x, y } = { ...a }
        var x1 = Math.cos(angle * MathUtils.DEG2RAD) * x - Math.sin(angle * MathUtils.DEG2RAD) * y
        var y1 = Math.cos(angle * MathUtils.DEG2RAD) * y + Math.sin(angle * MathUtils.DEG2RAD) * x
        return { x1, y1 }
    }

    // 左顶点和相邻右顶点夹角
    getNearBranchByLeft(lt, rt) {
        let ltAngle = Math.atan2(lt.y, lt.x) * 180 / Math.PI // 分支左上角角度
        let rtAngle = Math.atan2(rt.y, rt.x) * 180 / Math.PI // 分支右上角角度
        let angle = rtAngle - ltAngle
        return angle < 0 ? 360 + angle : angle
    }

    // 右顶点和相邻左顶点夹角
    getNearBranchByRight(rt, lt) {
        let ltAngle = Math.atan2(lt.y, lt.x) * 180 / Math.PI // 分支左上角角度
        let rtAngle = Math.atan2(rt.y, rt.x) * 180 / Math.PI // 分支右上角角度
        return ltAngle - rtAngle
    }



    // 根据两点坐标获取偏转角度
    getVector(a, b) {
        const y = b.z - a.z
        const x = b.x - a.x
        let angle = Math.atan2(y, x) * 180 / Math.PI
        return angle
    }

    // 根据两点坐标获取偏转角度
    getVectorT(a, b) {
        const y = b.y - a.y
        const x = b.x - a.x
        let angle = Math.atan2(y, x) * 180 / Math.PI
        return angle
    }


    // 创建几何图形
    createPlane(width, height, cssColor, pos, rot) {
        const element = document.createElement('div');
        element.style.width = width + 'px';
        element.style.height = height + 'px';
        // element.style.opacity = 0.75;
        element.style.background = cssColor;
        element.style.fillColor = 'rgba(255,255,255,0.25)';

        const object = new CSS3DObject(element);
        object.position.copy(pos);
        object.rotation.copy(rot);
        const material = new MeshBasicMaterial({
            color: 0x333333,
            wireframe: true,
            wireframeLinewidth: 1
        });

        const geometry = new PlaneGeometry(width, height);
        const mesh = new Mesh(geometry, material);
        mesh.position.copy(object.position);
        mesh.rotation.copy(object.rotation);
        return {
            object,
            mesh
        }
    }

    // 创建直线
    createLine(start, end, color) {
        const points = [];
        points.push(start.x, start.y, start.z);
        points.push(end.x, end.y, end.z);
        const geometry = new LineGeometry()
        geometry.setPositions(points)
        const material = new LineMaterial({ color, linewidth: 2, opacity: .7 });
        material.resolution.set(window.innerWidth, window.innerHeight);
        material.depthTest = false
        const line = new Line2(geometry, material);
        line.renderOrder = 9
        line.computeLineDistances();
        return line
    }

    // 创建虚线
    createDashedLine(start, end, color) {
        const points = [];
        points.push(start.x, start.y, start.z);
        points.push(end.x, end.y, end.z);
        const geometry = new LineGeometry()
        geometry.setPositions(points)
        const material = new LineMaterial({ color, linewidth: 4, dashSize: 10, gapSize: 2, opacity: .7 });
        material.defines.USE_DASH = ""
        // const lineDashedMaterial = new LineDashedMaterial( { vertexColors: true, scale: 2, dashSize: 1, gapSize: 1 } );
        material.resolution.set(window.innerWidth, window.innerHeight);
        material.depthTest = false
        const line = new Line2(geometry, material);
        line.renderOrder = 9
        line.computeLineDistances();
        return line
    }


    // 直线相交算法
    segmentsIntr(a, b, c, d) {
        /** 1 解线性方程组, 求线段交点. **/
        // 如果分母为0 则平行或共线, 不相交  
        var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
        if (denominator == 0) {
            return false;
        }

        // 线段所在直线的交点坐标 (x , y)      
        var x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) +
            (b.y - a.y) * (d.x - c.x) * a.x -
            (d.y - c.y) * (b.x - a.x) * c.x) / denominator;
        var y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x) +
            (b.x - a.x) * (d.y - c.y) * a.y -
            (d.x - c.x) * (b.y - a.y) * c.y) / denominator;

        /** 2 判断交点是否在两条线段上 **/
        if (
            // 交点在线段1上  
            (x - a.x) * (x - b.x) <= 0 && (y - a.y) * (y - b.y) <= 0
            // 且交点也在线段2上  
            &&
            (x - c.x) * (x - d.x) <= 0 && (y - c.y) * (y - d.y) <= 0
        ) {
            // 返回交点p
            return {
                x: x,
                y: y
            }
        }
        //否则不相交  
        return false
    }

    // 获取四个角度旋转后的坐标
    getRotateDeg(item) {
        const angle = item.angle // 旋转角度
        let obj = {}; // 定义左上，右上,左下,右下坐标
        obj.lt = this.getCoords(angle, {
            x: item.xIn,
            y: this.H
        }, item.obliqueLeft)
        obj.lb = this.getCoords(angle, {
            x: item.xIn,
            y: 0
        }, item.obliqueLeft)
        obj.rt = this.getCoords(angle, {
            x: -item.xOut,
            y: this.H
        }, item.obliqueRight)
        obj.rb = this.getCoords(angle, {
            x: -item.xOut,
            y: 0
        }, item.obliqueRight)
        return obj
    }

    // 根据坐标和角度获取旋转后的坐标
    getCoords(angle, coord, oblique) {
        let x, y
        let ange = Math.atan2(coord.y, coord.x) * 180 / Math.PI // 本身偏移量
        angle = ange - angle // 最后偏移的角度

        if (coord.y == 0) {
            // 如果y值为0则旋转时候不根据this.Z算半径
            x = Math.abs(coord.x) * Math.cos(angle * Math.PI / 180)
            y = Math.abs(coord.x) * Math.sin(angle * Math.PI / 180)
        } else {
            x = oblique * Math.cos(angle * Math.PI / 180)
            y = oblique * Math.sin(angle * Math.PI / 180)
        }
        return {
            x,
            y
        }
    }

    // 创建分支
    setCross(item) {
        let Y = this.H

        // 计算分支宽度
        const outLanes = item.lanes.filter(item => item.isOut) // 出车道
        const inLanes = item.lanes.filter(item => !item.isOut) // 近车道
        item.xIn = inLanes.length * this.laneWidth // 入的宽度
        item.xOut = outLanes.length * this.laneWidth // 出的宽度
        item.obliqueLeft = Math.sqrt(item.xIn * item.xIn + Y * Y) // 入车道原点到左上的距离
        item.obliqueRight = Math.sqrt(item.xOut * item.xOut + Y * Y) // 入车道原点到左上的距离

        const geometry = new BufferGeometry();
        geometry.needsUpdate = true;
        const vertices = new Float32Array([
            item.xIn, 0, 0,
            -item.xOut, 0, 0,
            -item.xOut, 0, Y,
            -item.xOut, 0, Y,
            item.xIn, 0, Y,
            item.xIn, 0, 0
        ]);
        geometry.setAttribute('position', new BufferAttribute(vertices, 3));
        const material = new MeshBasicMaterial({
            color: 0x00000,
            side: DoubleSide
        });
        const mesh = new Mesh(geometry, material);
        mesh.renderOrder = 2
        mesh.material.depthTest = false;
        return mesh;
    }

    render() {
        window.requestAnimationFrame(() => this.render());
        this.camera.updateProjectionMatrix();
        this.renderer2.render(this.scene2, this.camera);
        this.renderer.render(this.scene, this.camera);
    }
}